package com.xiaoshuidi.cloud.module.report.xxljob;

import com.xiaoshuidi.cloud.framework.tenant.core.aop.TenantIgnore;
import com.xiaoshuidi.cloud.module.report.constant.ContractStatusEnum;
import com.xiaoshuidi.cloud.module.report.constant.DiscountTypeEnum;
import com.xiaoshuidi.cloud.module.report.constant.FlowTypeEnum;
import com.xiaoshuidi.cloud.module.report.constant.PublicUseConstant;
import com.xiaoshuidi.cloud.module.report.dal.DO.ContractOrderFeesDO;
import com.xiaoshuidi.cloud.module.report.dal.DO.ContractSettlementDetailDO;
import com.xiaoshuidi.cloud.module.report.dal.DO.RoomHouseDO;
import com.xiaoshuidi.cloud.module.report.dal.DO.report.*;
import com.xiaoshuidi.cloud.module.report.dal.mapper.*;
import com.xiaoshuidi.cloud.module.report.dal.mapper.report.IncomeDetailDayReportMapper;
import com.xiaoshuidi.cloud.module.report.dal.mapper.report.ReportCircleFeeMapper;
import com.xiaoshuidi.cloud.module.report.service.contract.ContractOperateService;
import com.xiaoshuidi.cloud.module.report.service.finance.IncomeDetailReportService;
import com.xiaoshuidi.cloud.module.report.utils.DateUtils;
import com.xiaoshuidi.cloud.module.report.utils.ThreadPoolUtils;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

/**
 * 收入汇总明细job每天凌晨0:30跑一次
 * 将昨天的合同账单数据进行明细处理  处理  统计到天 明细
 */
@Component
@Slf4j
public class IncomeDetailDayHandleJob {
    //流入
    private static final Integer OUTFLOW = 1;

    @Resource
    private ContractOperateService contractOperateService;
    @Resource
    private IncomeDetailReportService incomeDetailReportService;
    @Resource
    private IncomeDetailDayReportMapper incomeDetailDayReportMapper;
    @Resource
    private RoomHouseMapper roomHouseMapper;
    @Resource
    private ContractRentorderMapper contractRentorderMapper;
    @Resource
    private ContractOrderfeesMapper contractOrderfeesMapper;
    @Resource
    private ContractSettlementMapper contractSettlementMapper;
    @Resource
    private ContractSettlementDetailMapper contractSettlementDetailMapper;
//    @Resource
//    private RoomSdiotDeviceMappingMapper roomSdiotDeviceMappingMapper;
//    @Resource
//    private DeviceDosageMapper deviceDosageMapper;
//    @Resource
//    private ReportDetailInFeesMapper reportDetailInFeesMapper;
    @Resource
    private ReportCircleFeeMapper reportCircleFeeMapper;

    // 用来修复数据
    @XxlJob("incomeDetailDayMonthHandleJob")
    @TenantIgnore
    public void execute2() {
        final String nowMonth = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
        String monthDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
        String params = XxlJobHelper.getJobParam();
        if(StringUtils.isNotBlank(params)){
            // 取参数
            String[] split = params.split("-");
            if (split.length != 2){
                log.error("参数格式错误");
                return;
            }
            monthDate = params;
        }
        List<LocalDate> dateList = new ArrayList<>();
        //判断是否当前月份
        if (nowMonth.equals(monthDate)){
            LocalDate today = LocalDate.now();
            LocalDate firstDayOfMonth = today.withDayOfMonth(1);
            LocalDate currentDay = firstDayOfMonth;
            while (!currentDay.isAfter(today)) {
                if (!currentDay.equals(today)){
                    dateList.add(currentDay);
                }
                currentDay = currentDay.plusDays(1);
            }
        }else {
            //指定月份第一天到最后一天
            LocalDate firstDayOfMonth = LocalDate.parse(monthDate + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            LocalDate endDate = firstDayOfMonth.withDayOfMonth(firstDayOfMonth.lengthOfMonth());
            LocalDate currentDay = firstDayOfMonth;
            while (!currentDay.isAfter(endDate)) {
                dateList.add(currentDay);
                currentDay = currentDay.plusDays(1);
            }
        }
        for (LocalDate date : dateList) {
            List<ReportContractInfoDO> list = contractOperateService.selectHaveIncomeContractsYesterday(date);
            long between = ChronoUnit.DAYS.between(date, LocalDate.now());
            int minusDays = (int)between;
            handlerContract(list, date,minusDays);
        }
    }


    @XxlJob("incomeDetailDayHandleJob")
    @TenantIgnore
    public void execute() {
        Integer minusDay = 1;
        String params = XxlJobHelper.getJobParam();
        if(StringUtils.isNotBlank(params)){
            try {
                minusDay = Integer.parseInt(params);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
        //获取昨天存在权责的合同
        final int minusDays = minusDay;
        final LocalDate yesterday = LocalDate.now().minusDays(minusDays);
        long totalNum = contractOperateService.countHaveIncomeContractsYesterday(yesterday);
        if (totalNum <= 1000) {
            //合同数<1000直接一次处理
            List<ReportContractInfoDO> list = contractOperateService.selectHaveIncomeContractsYesterday(yesterday);
            handlerContract(list, yesterday,minusDays);
        } else {
            //分1000一批处理
            int pageNo = 0;
            int pageSize = 1000;
            List<ReportContractInfoDO> list = null;
            int num = (int)totalNum/pageSize + 1 ;
            CountDownLatch latch = new CountDownLatch(num);
            do {
                int from = pageNo * pageSize;
                list = contractOperateService.selectHaveIncomeContractsYesterdayInTurn(yesterday, from, pageSize);
                final List<ReportContractInfoDO> finalList = list;
                pageNo++;
                ThreadPoolUtils.poolExecutor.execute(()->{
                    handlerContract(finalList, yesterday,minusDays);
                    latch.countDown();
                });
            } while (list.size() == 1000);
            try {
                log.info("---------------处理生成收入日明细完成-------------");
                latch.await();
            }catch (InterruptedException e){
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 单个合同执行流程
     */
    void handlerContract(List<ReportContractInfoDO> list, LocalDate yesterday,int i) {
        if(CollectionUtils.isEmpty(list)){
            return;
        }
        final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDateTime startTime = DateUtils.getBeginDayOfYesterday(i);
        LocalDateTime endTime = DateUtils.getEndDayOfYesterDay(i);
        String currentMonth = DateUtils.getYesterdayMonthAnI(i);
        List<Long> roomIds = list.stream().map(ReportContractInfoDO::getRoomId).collect(Collectors.toList());
        List<RoomHouseDO> houseList = roomHouseMapper.selectByIds(roomIds);
//        List<RoomSdiotDevicePO> roomSdiotDeviceList = roomSdiotDeviceMappingMapper.selectBatchByRoomIds(roomIds);
        for (ReportContractInfoDO infoDO : list) {
            IncomeDetailDayReportDO detailDO = new IncomeDetailDayReportDO();
            detailDO.setContractId(infoDO.getContractId());
            detailDO.setContractNo(infoDO.getContractNo());
            detailDO.setCustomerName(infoDO.getCustomerName());
            detailDO.setApartmentId(infoDO.getApartmentId());
            detailDO.setApartmentName(infoDO.getApartmentName());
            //拼凑房源名称
            RoomHouseDO houseDO = houseList.stream().filter(e -> e.getId().equals(infoDO.getRoomId())).findAny().orElse(null);
            if (houseDO == null) {
                log.error("====脏数据 此房间id:{}无房间信息",infoDO.getRoomId());
                continue;
            }
            detailDO.setArea(houseDO.getArea());
            detailDO.setRoomId(houseDO.getId());
            detailDO.setRoomName(houseDO.getName());
            detailDO.setCity(houseDO.getCity());
            detailDO.setIsConcentrated(houseDO.getIsConcentrated());
            detailDO.setDistrict(houseDO.getDistrict());
            //集中式和分散式拼凑方法不一样
            StringBuilder houseName = new StringBuilder(houseDO.getArea()).append(houseDO.getBuildingNo()).append("栋");
            if (houseDO.getIsConcentrated()) {
                //集中式 小区名-楼栋-房源名称   room_house.area - room_house.building_no栋 - room_house.name
                detailDO.setHouseResource(houseName.append(houseDO.getName()).toString());
            } else {
                //分散式  小区名-楼栋-单元-房源名称 room_house.area - room_house.building_no栋 - room_house.unit_no单元 - room_house.name
                detailDO.setHouseResource(houseName.append(houseDO.getUnitNo()).append("单元").append(houseDO.getName()).toString());
            }
            detailDO.setContractPeriod(dateFormat.format(infoDO.getStartTime()) + "~" + dateFormat.format(infoDO.getEndTime()));
            //计算合同对应的租金
            Map<String, String> amountMap = handlerRent(infoDO, yesterday);
            detailDO.setCurrMonthDays(Integer.parseInt(amountMap.get("days")));
            detailDO.setStatisticsMonth(currentMonth);
            detailDO.setCurRentIn(new BigDecimal(amountMap.get("dayRent")));
            detailDO.setPropertyFee(new BigDecimal(amountMap.get("dayProperty")));
            detailDO.setCurRentDisc(new BigDecimal(amountMap.get("dayDiscount")));
            detailDO.setCurRentReduce(detailDO.getCurRentIn().subtract(detailDO.getCurRentDisc()));
            //查询合同在当前月存在的账单，并计算其明细
            //能耗费（电费、冷水费、热水费、燃气费、暖气费、）、服务费、保洁费、物业费、上网费、清洁费、换房费、停车费、违约金
            //分账单表和结算单表
            //根据合同id查询已支付的账单数据
            /**
             *各种费项，iot费用，流入流出
             * 费项里 周期性费用 权责算到每一天
             */
//            List<RoomSdiotDevicePO> sdiotDevicePOList = roomSdiotDeviceList.stream().filter(e -> e.getRoomHouseId().equals(infoDO.getRoomId())).collect(Collectors.toList());
            //TODO 处理费项
            handlerOrderFees(infoDO, detailDO, startTime, endTime,yesterday,i);
            detailDO.setCurOtherIn(detailDO.getCurOtherIn().add(detailDO.getPropertyFee()));
            detailDO.setCurOtherReduce(detailDO.getCurOtherReduce().add(detailDO.getPropertyFee()));
            //查询合同最近一个月的累计
            IncomeDetailDayReportDO recentIncomeReport = incomeDetailDayReportMapper.getRecentIncomeReport(infoDO.getContractId());
            if (recentIncomeReport != null) {
                detailDO.setAccRentIn(detailDO.getCurRentIn().add(recentIncomeReport.getAccRentIn()));
                detailDO.setAccRentDisc(detailDO.getCurRentDisc().add(recentIncomeReport.getAccOtherReduce()));
                detailDO.setAccRentReduce(detailDO.getCurRentReduce().add(recentIncomeReport.getAccRentReduce()));
                detailDO.setAccOtherIn(detailDO.getCurOtherIn().add(recentIncomeReport.getAccOtherIn()));
                detailDO.setAccOtherDisc(detailDO.getCurOtherDisc().add(recentIncomeReport.getAccOtherDisc()));
                detailDO.setAccOtherReduce(detailDO.getCurOtherReduce().add(recentIncomeReport.getAccOtherReduce()));
            } else {
                detailDO.setAccRentIn(detailDO.getCurRentIn());
                detailDO.setAccRentDisc(detailDO.getCurRentDisc());
                detailDO.setAccRentReduce(detailDO.getCurRentReduce());
                detailDO.setAccOtherIn(detailDO.getCurOtherIn());
                detailDO.setAccOtherDisc(detailDO.getCurOtherDisc());
                detailDO.setAccOtherReduce(detailDO.getCurOtherReduce());
            }
            detailDO.setTenantId(infoDO.getTenantId());
            detailDO.setDeptId(infoDO.getDeptId());
            //TODO 如果此合同之前没有权责，需判断是否是补录合同， 补录合同需把历史权责记录在昨天
            handleRecordIngContract(detailDO,infoDO,startTime,yesterday);
            incomeDetailDayReportMapper.insert(detailDO);
//            if (!CollectionUtils.isEmpty(feesDOList)) {
//                reportDetailInFeesMapper.insertBatch(feesDOList);
//            }
        }
    }

    /**
     * 处理补录合同的权责
     */
    private void handleRecordIngContract(IncomeDetailDayReportDO detailDO,ReportContractInfoDO infoDO,LocalDateTime startTime,LocalDate yesterDay){
        if(infoDO.getActualEndTime()!=null && infoDO.getActualEndTime().isBefore(startTime.toLocalDate())){
            startTime = infoDO.getActualEndTime().atTime(23,59);
        }else if(infoDO.getActualEndTime()==null && infoDO.getEndTime().isBefore(startTime.toLocalDate()) &&
                (ContractStatusEnum.ABNORMAL_END.getStatus().equals(infoDO.getContractStatus())||ContractStatusEnum.NORMAL_END.getStatus().equals(infoDO.getContractStatus()))){
            startTime = infoDO.getEndTime().atTime(23,59);
        }
        LocalDate towDaysAgo = startTime.toLocalDate().minusDays(1);
        List<IncomeDetailDayReportDO> incomeDetailDayReportDOS = incomeDetailDayReportMapper.selectByContractId(infoDO.getContractId());
        if(CollectionUtils.isEmpty(incomeDetailDayReportDOS)){
            if(infoDO.getCreateTime().isAfter(infoDO.getStartTime().atTime(23,59))&&!infoDO.getStartTime().isAfter(startTime.toLocalDate().minusDays(1))){
                //查询权责从合同最开始到前天之间的权责
                int dayOfMonth = towDaysAgo.getDayOfMonth();
                int dayLength = 0;
                //如果前天是账单的上半部分，则按照前天对应的上月月租金进行计算
                DateUtils.MonthAndRemainder monthAndRemainder = DateUtils.monthsAndRemainder(infoDO.getStartTime(), startTime.toLocalDate().minusDays(1));
                int day = infoDO.getStartTime().minusMonths(-monthAndRemainder.getMonth()).minusDays(1).getDayOfMonth();
                if(day>=dayOfMonth){
                    dayLength = towDaysAgo.minusMonths(1).lengthOfMonth();
                }else {
                    dayLength = towDaysAgo.lengthOfMonth();
                }
                ReportContractDayRentDO reportContractDayRentDO = contractOperateService.selectByContratIdAndDays(infoDO.getContractId(), dayLength);
                if(reportContractDayRentDO==null){
                    return;
                }
                BigDecimal dayRent = reportContractDayRentDO.getNormalDayRent().multiply(BigDecimal.valueOf(monthAndRemainder.getRemainder()));
                BigDecimal dayProperty = reportContractDayRentDO.getNormalDayProperty().multiply(BigDecimal.valueOf(monthAndRemainder.getRemainder()));
                BigDecimal totalMonthRent = dayRent.add(BigDecimal.valueOf(monthAndRemainder.getMonth()).multiply(infoDO.getMonthRent()));
                BigDecimal totalMonthProperty = dayProperty.add(BigDecimal.valueOf(monthAndRemainder.getMonth()).multiply(infoDO.getProperty()));
                detailDO.setCurRentIn(detailDO.getCurRentIn().add(totalMonthRent));
                detailDO.setPropertyFee(detailDO.getPropertyFee().add(totalMonthProperty));
                //计算租金折扣
                BigDecimal dayDiscount = reportContractDayRentDO.getNormalDayDiscount().multiply(BigDecimal.valueOf(monthAndRemainder.getRemainder()));
                BigDecimal totalMonthDiscount = dayDiscount.add(BigDecimal.valueOf(monthAndRemainder.getMonth()).multiply(infoDO.getDiscountValue()));
                detailDO.setCurRentDisc(detailDO.getCurRentDisc().add(totalMonthDiscount));
                detailDO.setCurRentReduce(detailDO.getCurRentIn().subtract(detailDO.getCurRentDisc()));
                detailDO.setAccRentIn(detailDO.getCurRentIn());
                detailDO.setAccRentDisc(detailDO.getCurRentDisc());
                detailDO.setAccRentReduce(detailDO.getCurRentReduce());
                //查询其他权责  所有已付账单
                List<Long> rentOrderIds = contractRentorderMapper.queryByContractIdOnPayed(detailDO.getContractId(),startTime.toLocalDate().atTime(23,59).minusDays(1L));
                if(!CollectionUtils.isEmpty(rentOrderIds)){
                    //根据账单id查询账单明细
                    List<ContractOrderFeesDO> orderFeesList = contractOrderfeesMapper.selectByOrderIds(rentOrderIds);
                    //计算其他收入和折扣
                    BigDecimal otherAmount = BigDecimal.ZERO;
                    BigDecimal otherDiscountAmount = BigDecimal.ZERO;

                    for (ContractOrderFeesDO feesDO : orderFeesList) {
                        if (OUTFLOW.equals(feesDO.getFlowType())) {
                            //流出取反
                            feesDO.setAmount(feesDO.getAmount().negate());
                            feesDO.setDiscountAmount(feesDO.getDiscountAmount().negate());
                        }
                        handlerAllFeesForEach(detailDO, feesDO.getFeeName(),feesDO.getAmount());
                        if(feesDO.getFeeId()!=1&&feesDO.getFeeId()!=2&&!feesDO.getFeeName().equals(PublicUseConstant.PROPERTY)){
                            otherAmount =otherAmount.add(feesDO.getAmount());
                            otherDiscountAmount =otherDiscountAmount.add(feesDO.getDiscountAmount());
                        }
                    }
                    detailDO.setCurOtherIn(otherAmount.add(detailDO.getPropertyFee()));
                    detailDO.setCurOtherDisc(otherDiscountAmount);
                    detailDO.setAccOtherIn(detailDO.getCurOtherIn());
                    detailDO.setAccOtherDisc(detailDO.getCurOtherDisc());
                }
                LocalDateTime dateTime = startTime.with(TemporalAdjusters.firstDayOfMonth());
                List<Long> settleIds = contractSettlementMapper.queryByContractIdOnPayedInPayTime(detailDO.getContractId(), dateTime, startTime);
                if (!CollectionUtils.isEmpty(settleIds)) {
                    //结算单费用
                    List<ContractSettlementDetailDO> settlementDetailList = contractSettlementDetailMapper.selectByOrderIds(settleIds);
                    for (ContractSettlementDetailDO settlementDetail : settlementDetailList) {
                        if(FlowTypeEnum.OUT.getCode().equals(settlementDetail.getFlowType())){
                            //收入不统计流出的钱
                            continue;
                        }
                        handlerAllFeesForEach(detailDO, settlementDetail.getFeeName(),settlementDetail.getRefundableAmount());

                        if (!PublicUseConstant.DEPOSIT.equals(settlementDetail.getFeeName()) && !PublicUseConstant.RENT.equals(settlementDetail.getFeeName())) {
                            //押金和租金不算权责里其他收入
                            detailDO.setCurOtherIn(detailDO.getCurOtherIn().add(settlementDetail.getRefundableAmount()));
                            detailDO.setAccOtherIn(detailDO.getCurOtherIn());
                        }
                    }
                }
                detailDO.setCurOtherReduce(detailDO.getCurOtherIn().subtract(detailDO.getCurOtherDisc()));
                detailDO.setAccOtherReduce(detailDO.getCurOtherReduce());
            }
        }
        //补录合同 是合同很早就开始但是是最近才录入
        if(infoDO.getCreateTime().toLocalDate().isAfter(infoDO.getStartTime())){
            if(DateUtils.inMonth(infoDO.getCreateTime().toLocalDate(),infoDO.getStartTime())){
                //是当月不算补录
                detailDO.setAdditionalFlag(false);
            }else {
                //否则算补录
                detailDO.setAdditionalFlag(true);
            }
        }else {
            detailDO.setAdditionalFlag(false);
        }
    }

    private void handlerAllFeesForEach(IncomeDetailDayReportDO detailDO, String feeName,BigDecimal amount) {
        IncomeDetailDayReportDO detailDO1 = new IncomeDetailDayReportDO();
        handlerAmountByFeeName(detailDO1, feeName, amount);
        detailDO.setColdWaterFee(detailDO.getColdWaterFee().add(detailDO1.getColdWaterFee()));
        detailDO.setHotWaterFee(detailDO.getHotWaterFee().add(detailDO1.getHotWaterFee()));
        detailDO.setElectricFee(detailDO.getElectricFee().add(detailDO1.getElectricFee()));
//        detailDO.setPropertyFee(detailDO.getPropertyFee().add(detailDO1.getPropertyFee()));
        detailDO.setMaintenanceFee(detailDO.getMaintenanceFee().add(detailDO1.getMaintenanceFee()));
        detailDO.setCleanFee(detailDO.getCleanFee().add(detailDO1.getCleanFee()));
        detailDO.setLiquidatedFee(detailDO.getLiquidatedFee().add(detailDO1.getLiquidatedFee()));
        detailDO.setDelayFee(detailDO.getDelayFee().add(detailDO1.getDelayFee()));
        detailDO.setDamages(detailDO.getDamages().add(detailDO1.getDamages()));
        detailDO.setHouseTransferFee(detailDO.getHouseTransferFee().add(detailDO1.getHouseTransferFee()));
    }

    /**
     * 处理账单明细和结算单明细中各种费项
     */
    public void handlerOrderFees(ReportContractInfoDO infoDO, IncomeDetailDayReportDO detailDO, LocalDateTime startTime, LocalDateTime endTime, LocalDate yesterday,int i) {
        List<Long> rentOrderIds = contractRentorderMapper.queryByContractIdOnPayedInPayTime(detailDO.getContractId(), startTime, endTime);
        BigDecimal otherIn = BigDecimal.ZERO;
        BigDecimal otherDiscount = BigDecimal.ZERO;
//        List<ReportDetailInFeesDO> feesDOList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(rentOrderIds)) {
            //根据账单id查询账单明细
            List<ContractOrderFeesDO> orderFeesList = contractOrderfeesMapper.selectByOrderIds(rentOrderIds);
            //账单明细各种费项 费用
            for (ContractOrderFeesDO feesDO : orderFeesList) {
//                ReportDetailInFeesDO detailInFeesDO = new ReportDetailInFeesDO();
                if (OUTFLOW.equals(feesDO.getFlowType())) {
                    //流出取反
                    feesDO.setAmount(feesDO.getAmount().negate());
                    feesDO.setDiscountAmount(feesDO.getDiscountAmount().negate());
                }
                //周期性费项 需均摊每一天
                if ("cycle".equals(feesDO.getFeeSort())) {
                    //TODO 计算周期性费用的均摊
                    circleFeeHandle(infoDO, feesDO);
                }

//                detailInFeesDO.setFeeCode(feesDO.getFeeId().toString());
//                detailInFeesDO.setFeeName(feesDO.getFeeName());
//                detailInFeesDO.setAmount(feesDO.getAmount());
//                detailInFeesDO.setDiscountAmount(feesDO.getDiscountAmount());
//                detailInFeesDO.setStatisticsMonth(DateUtils.getYesterdayMonthAnI(i));
//                detailInFeesDO.setCreateTime(feesDO.getCreateTime());
//                detailInFeesDO.setTenantId(feesDO.getTenantId());
//                feesDOList.add(detailInFeesDO);
                handlerAmountByFeeName(detailDO, feesDO.getFeeName(),feesDO.getAmount());
                if (!PublicUseConstant.DEPOSIT.equals(feesDO.getFeeName()) && !PublicUseConstant.RENT.equals(feesDO.getFeeName()) && !PublicUseConstant.PROPERTY.equals(feesDO.getFeeName())) {
                    //押金和租金不算权责里其他收入
                    otherIn = otherIn.add(feesDO.getAmount());
                    otherDiscount = otherDiscount.add(feesDO.getDiscountAmount());
                }
            }
        }
        List<Long> settleIds = contractSettlementMapper.queryByContractIdOnPayedInPayTime(detailDO.getContractId(), startTime, endTime);
        if (!CollectionUtils.isEmpty(settleIds)) {
            //结算单费用
            List<ContractSettlementDetailDO> settlementDetailList = contractSettlementDetailMapper.selectByOrderIds(settleIds);
            for (ContractSettlementDetailDO settlementDetail : settlementDetailList) {
//                ReportDetailInFeesDO detailInFeesDO = new ReportDetailInFeesDO();
//                detailInFeesDO.setFeeCode(settlementDetail.getFeeId()+"");
//                detailInFeesDO.setFeeName(settlementDetail.getFeeName());
//                detailInFeesDO.setAmount(settlementDetail.getAmount());
//                detailInFeesDO.setDiscountAmount(BigDecimal.ZERO);
//                detailInFeesDO.setStatisticsMonth(DateUtils.getYesterdayMonthAnI(i));
//                detailInFeesDO.setCreateTime(settlementDetail.getCreateTime());
//                feesDOList.add(detailInFeesDO);
                if(FlowTypeEnum.OUT.getCode().equals(settlementDetail.getFlowType())){
                    //收入不统计流出的钱
                    continue;
                }
                handlerAmountByFeeName(detailDO,settlementDetail.getFeeName(),settlementDetail.getRefundableAmount());
                if (!PublicUseConstant.DEPOSIT.equals(settlementDetail.getFeeName()) && !PublicUseConstant.RENT.equals(settlementDetail.getFeeName())
                        && !PublicUseConstant.PROPERTY.equals(settlementDetail.getFeeName())) {
                    //押金和租金不算权责里其他收入
                    otherIn = otherIn.add(settlementDetail.getAmount());
                }
            }
        }

        //iot费用
//        if (!CollectionUtils.isEmpty(sdiotDevicePOList)) {
//            for (RoomSdiotDevicePO sdiotDevicePO : sdiotDevicePOList) {
//                List<DeviceDosageDO> dosageList = deviceDosageMapper.selectListByDeviceNo(sdiotDevicePO.getDeviceNo());
//                if (!CollectionUtils.isEmpty(dosageList)) {
//                    BigDecimal iotAmount = dosageList.stream().map(DeviceDosageDO::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
//                    ReportDetailInFeesDO detailInFeesDO = new ReportDetailInFeesDO();
//                    detailInFeesDO.setAmount(iotAmount);
//                    detailInFeesDO.setFeeCode(sdiotDevicePO.getDeviceType());
//                    detailInFeesDO.setFeeName(IotTypeEnum.getValueByMonth(sdiotDevicePO.getDeviceType()));
//                    detailInFeesDO.setDiscountAmount(BigDecimal.ZERO);
//                    detailInFeesDO.setStatisticsMonth(DateUtils.getYesterdayMonthAnI(i));
//                    detailInFeesDO.setCreateTime(dosageList.get(0).getCreateTime());
//                    feesDOList.add(detailInFeesDO);
//                }
//
//            }
//        }
        /**
         * 判断当前合同在时间范围内是否存在 周期性费用 且需判断应该给算结余费用还是均摊费用
         */
        List<ReportCircleFeeDO> circleFeeDOS = reportCircleFeeMapper.selectByContractIdAndTime(infoDO.getContractId(), yesterday);
        if(!CollectionUtils.isEmpty(circleFeeDOS)){
            for (ReportCircleFeeDO circleFeeDO : circleFeeDOS) {
//                ReportDetailInFeesDO detailInFeesDO = new ReportDetailInFeesDO();
//                detailInFeesDO.setFeeCode(circleFeeDO.getFeeCode());
//                detailInFeesDO.setFeeName(circleFeeDO.getFeeName());
//                detailInFeesDO.setDiscountAmount(BigDecimal.ZERO);
//                detailInFeesDO.setStatisticsMonth(DateUtils.getYesterdayMonthAnI(i));
//                detailInFeesDO.setCreateTime(LocalDateTime.now());
                if(yesterday.isBefore(circleFeeDO.getEndTime())){
//                    detailInFeesDO.setAmount(circleFeeDO.getAverAmount());
                    otherIn = otherIn.add(circleFeeDO.getAverAmount());
                }else if(yesterday.isEqual(circleFeeDO.getEndTime())){
//                    detailInFeesDO.setAmount(circleFeeDO.getAmountBalance());
                    otherIn = otherIn.add(circleFeeDO.getAmountBalance());
                }
//                feesDOList.add(detailInFeesDO);
            }
        }
        otherIn = otherIn.add(detailDO.getHouseTransferFee());
        detailDO.setCurOtherIn(otherIn);
        detailDO.setCurOtherDisc(otherDiscount);
        detailDO.setCurOtherReduce(otherIn.subtract(otherDiscount));
//        return feesDOList;
    }

    private void handlerAmountByFeeName(IncomeDetailDayReportDO detailDO,String feeName,BigDecimal amount) {
        switch (feeName) {
            case PublicUseConstant.COLD_WATER:
                if (detailDO.getColdWaterFee() == null) {
                    detailDO.setColdWaterFee(amount);
                } else {
                    detailDO.setColdWaterFee(amount.add(detailDO.getColdWaterFee()));
                }
                break;
            case PublicUseConstant.HOT_WATER:
                detailDO.setHotWaterFee(amount);
                break;
            case PublicUseConstant.ELECTRICITY:
                detailDO.setElectricFee(amount);
                break;
            case PublicUseConstant.PROPERTY:
                //均摊每一天 按权责算
//                detailDO.setPropertyFee(amount);
                break;
            case PublicUseConstant.REPAIR:
                detailDO.setMaintenanceFee(amount);
                break;
            case PublicUseConstant.CLEAN:
                detailDO.setCleanFee(amount);
                break;
            case PublicUseConstant.LIQUIDATED:
                detailDO.setLiquidatedFee(amount);
                break;
            case PublicUseConstant.DELAY:
                detailDO.setDelayFee(amount);
                break;
            case PublicUseConstant.DAMAGES:
                detailDO.setDamages(amount);
                break;
            case PublicUseConstant.HOUSE_TRANSFER:
                detailDO.setHouseTransferFee(amount);
                break;
            case PublicUseConstant.ROOM_TRANSFER:
                detailDO.setHouseTransferFee(amount);
                break;
            case PublicUseConstant.LATEFEE:
                detailDO.setLateFee(amount);
                break;
            default:
        }
    }

    private void circleFeeHandle(ReportContractInfoDO infoDO, ContractOrderFeesDO feesDO) {
        long daySpace = DateUtils.getDaySpace(infoDO.getStartTime(), infoDO.getEndTime());
        BigDecimal averAmount = feesDO.getAmount().divide(BigDecimal.valueOf(daySpace));
        BigDecimal amountBalance = feesDO.getAmount().subtract(averAmount.multiply(BigDecimal.valueOf(daySpace-1)));
        ReportCircleFeeDO circleFeeDO = new ReportCircleFeeDO();
        circleFeeDO.setAmount(feesDO.getAmount());
        circleFeeDO.setFeeName(feesDO.getFeeName());
        circleFeeDO.setContractId(infoDO.getContractId());
        circleFeeDO.setOrderId(feesDO.getOrderId());
        circleFeeDO.setStartTime(feesDO.getStartTime());
        circleFeeDO.setEndTime(feesDO.getEndTime());
        circleFeeDO.setAverAmount(averAmount);
        circleFeeDO.setAmountBalance(amountBalance);
        reportCircleFeeMapper.insert(circleFeeDO);
        feesDO.setAmount(averAmount);
    }


    /**
     * 计算租金 假如月租金1000
     * 按日租金算  3.15-4.14 分两段  3.1-3.15,3.16-3.31,4.1-4.15,4.16-4.30 且每个月上半段按上月的租金/上月的天数
     * 如4.1-4.15 日租金是 按3月天数31天，1000/31是4.1-4.15的日租金
     * 退租需注意特殊场景 例如4.25-5.24 可能5.27退租也可能6.3退租 此时的延期退租金额计算方式不一样
     *
     * @param infoDO
     */
    public Map<String, String> handlerRent(ReportContractInfoDO infoDO, LocalDate yesterday) {
        //查询合同对应的日租金情况
        //计算优惠金额
        if(infoDO.getActualEndTime()==null && (ContractStatusEnum.ABNORMAL_END.getStatus().equals(infoDO.getContractStatus())||ContractStatusEnum.NORMAL_END.getStatus().equals(infoDO.getContractStatus()))){
            //处理脏数据，退租了没有退租时间
            infoDO.setActualEndTime(infoDO.getEndTime());
        }

        BigDecimal discountValue = BigDecimal.ZERO;
        BigDecimal monthRent = infoDO.getMonthRent();
        if (StringUtils.isNotBlank(infoDO.getDiscountSort()) && StringUtils.isNotBlank(infoDO.getDiscountType())) {
            if (DiscountTypeEnum.RATE.getType().equals(infoDO.getDiscountType())) {
                discountValue = monthRent.multiply(infoDO.getDiscountValue()).divide(BigDecimal.valueOf(100));
            } else {
                discountValue = infoDO.getDiscountValue();
            }
        }
//        if(BigDecimal.ZERO.compareTo(discountValue)!=0 && infoDO.getPayMethodF()!=null && infoDO.getPayMethodF()!=0){
//            discountValue = discountValue.divide(BigDecimal.valueOf(infoDO.getPayMethodF()),2,RoundingMode.HALF_UP);
//        }
        infoDO.setDiscountValue(discountValue);
        //保存日租金及周期优惠 方便其他需要时直接获取
        List<ReportContractDayRentDO> dayRentDOS = processDayRent(infoDO);
        //昨天所在月份拥有的天数
        int pastDays = yesterday.lengthOfMonth();
        //获取昨天所在的天数
        int pastDayNum = yesterday.getDayOfMonth();
        //获取昨天对应的上月所在天数
        int lastMonthDays = yesterday.minusMonths(1).lengthOfMonth();
        //昨天与合同开始日相差多少个月
        DateUtils.MonthAndRemainder monthAndRemainder = DateUtils.monthsAndRemainder(infoDO.getStartTime(), yesterday);
        //计算昨天所在的月份对应的 结束日期
        int monthSub = monthAndRemainder.getMonth();
        //TODO 1号开始的结束日期 endDays有变数
        int endDays = infoDO.getStartTime().plusMonths(monthSub).plusDays(-1).getDayOfMonth();
        if(infoDO.getStartTime().getDayOfMonth()==1){
            endDays = yesterday.lengthOfMonth();
        }
        //a.1 获取日租金
        BigDecimal dayRent = BigDecimal.ZERO;
        BigDecimal dayProperty = BigDecimal.ZERO;
        BigDecimal dayDiscount = BigDecimal.ZERO;
        ReportContractDayRentDO yesterdayMonth = new ReportContractDayRentDO();
        ReportContractDayRentDO lastDayMonth = new ReportContractDayRentDO();
        for (ReportContractDayRentDO dayRentDO : dayRentDOS) {
            if (pastDays == dayRentDO.getMonthDays()) {
                yesterdayMonth = dayRentDO;
            }
            if (lastMonthDays == dayRentDO.getMonthDays()) {
                lastDayMonth = dayRentDO;
            }
        }
        if (yesterday.isBefore(infoDO.getEndTime())) {
            //正常房租
            if (pastDayNum < endDays) {
                if(infoDO.getStartTime().getDayOfMonth()==1){
                    dayRent = yesterdayMonth.getNormalDayRent();
                    dayProperty = yesterdayMonth.getNormalDayProperty();
                    dayDiscount = yesterdayMonth.getNormalDayDiscount();
                }else {
                    dayRent = lastDayMonth.getNormalDayRent();
                    dayProperty = lastDayMonth.getNormalDayProperty();
                    dayDiscount = lastDayMonth.getNormalDayDiscount();
                }
            } else if (pastDayNum == endDays) {
                if(infoDO.getStartTime().getDayOfMonth()==1){
                    //如果合同开始日是1号，则昨天所在的日租金是按当月的算
                    dayRent = yesterdayMonth.getDayRentBalance();
                    dayProperty = yesterdayMonth.getDayPropertyBalance();
                    dayDiscount = yesterdayMonth.getDayDiscountBalance();
                }else {
                    dayRent = lastDayMonth.getDayRentBalance();
                    dayProperty = lastDayMonth.getDayPropertyBalance();
                    dayDiscount = lastDayMonth.getDayDiscountBalance();
                }

            } else {
                dayRent = yesterdayMonth.getNormalDayRent();
                dayProperty = yesterdayMonth.getNormalDayProperty();
                dayDiscount = yesterdayMonth.getNormalDayDiscount();
            }
        } else if (yesterday.isEqual(infoDO.getEndTime())) {
            if(infoDO.getStartTime().getDayOfMonth()==1){
                //如果合同开始日是1号，则昨天所在的日租金是按当月的算
                dayRent = yesterdayMonth.getDayRentBalance();
                dayProperty = yesterdayMonth.getDayPropertyBalance();
                dayDiscount = yesterdayMonth.getDayDiscountBalance();
            }else {
                dayRent = lastDayMonth.getDayRentBalance();
                dayProperty = lastDayMonth.getDayPropertyBalance();
                dayDiscount = lastDayMonth.getDayDiscountBalance();
            }
        } else {
            //此时逾期了
            if(infoDO.getActualEndTime()!=null&&yesterday.isAfter(infoDO.getActualEndTime())){
                //逾期且合同结束 此种情况只存在 补录合同且是本地首次跑历史的数据时才会走到
                dayRent = lastDayMonth.getDayRentBalance();
            }else if(infoDO.getActualEndTime()!=null&&infoDO.getActualEndTime().isEqual(infoDO.getEndTime())){
                dayRent = yesterdayMonth.getNormalDayRent();
                dayProperty = yesterdayMonth.getNormalDayProperty();
                dayDiscount = yesterdayMonth.getNormalDayDiscount();
            }else {
                //已经结束了 只能算逾期
                //TODO 后期算逾期租金 修改此处逻辑
                dayRent = yesterdayMonth.getNormalDayRent();
                dayProperty = yesterdayMonth.getNormalDayProperty();
                dayDiscount = yesterdayMonth.getNormalDayDiscount();
            }
        }
        Map<String, String> amountMap = new HashedMap();
        //日租金 日折扣, 统计天数
        amountMap.put("dayRent", String.valueOf(dayRent));
        amountMap.put("dayDiscount", String.valueOf(dayDiscount));
        amountMap.put("dayProperty", String.valueOf(dayProperty));
        if (DateUtils.inMonth(yesterday, infoDO.getStartTime())) {
            amountMap.put("days", String.valueOf(pastDayNum - infoDO.getStartTime().getDayOfMonth()+1));
        } else {
            amountMap.put("days", String.valueOf(pastDayNum));
        }
        return amountMap;
    }

    /**
     * 保存/获取日租金及优惠
     */
    private List<ReportContractDayRentDO> processDayRent(ReportContractInfoDO infoDO) {
        List<ReportContractDayRentDO> dayRentList = contractOperateService.selectListByContractId(infoDO.getContractId());
        if (!CollectionUtils.isEmpty(dayRentList)) {
            return dayRentList;
        }
        BigDecimal dicountValue = infoDO.getDiscountValue();
        BigDecimal monthRent = infoDO.getMonthRent();
        BigDecimal property = infoDO.getProperty();
        //计算合同日租金
        List<ReportContractDayRentDO> list = new ArrayList<>(4);
        for (int i = 28; i <= 31; i++) {
            ReportContractDayRentDO dayRentDO = new ReportContractDayRentDO();
            dayRentDO.setContractId(infoDO.getContractId());
            dayRentDO.setMonthRent(infoDO.getMonthRent());
            dayRentDO.setMonthDays(i);
            dayRentDO.setTenantId(infoDO.getTenantId());
            BigDecimal normalDayRent = monthRent.divide(BigDecimal.valueOf(i), 2, RoundingMode.HALF_UP);
            BigDecimal normalDayProperty = property.divide(BigDecimal.valueOf(i), 2, RoundingMode.HALF_UP);
            BigDecimal dayRentBalance = monthRent.subtract(normalDayRent.multiply(BigDecimal.valueOf(i - 1)));
            BigDecimal dayPropertyBalance = property.subtract(normalDayProperty.multiply(BigDecimal.valueOf(i - 1)));
            if (BigDecimal.ZERO.compareTo(dicountValue) == 0) {
                dayRentDO.setDayDiscountBalance(BigDecimal.ZERO);
                dayRentDO.setNormalDayDiscount(BigDecimal.ZERO);
            } else {
                BigDecimal normalDayDiscount = dicountValue.divide(BigDecimal.valueOf(i), 2, RoundingMode.HALF_UP);
                dayRentDO.setNormalDayDiscount(normalDayDiscount);
                dayRentDO.setDayDiscountBalance(dicountValue.subtract(normalDayDiscount.multiply(BigDecimal.valueOf(i - 1))));
            }
            dayRentDO.setNormalDayRent(normalDayRent);
            dayRentDO.setNormalDayProperty(normalDayProperty);
            dayRentDO.setDayRentBalance(dayRentBalance);
            dayRentDO.setDayPropertyBalance(dayPropertyBalance);
            list.add(dayRentDO);
        }
        contractOperateService.batchSaveContractDayRent(list);
        return list;
    }
}
